<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <!-- 网页信息 -->
    <link rel="icon" href="/favicon.ico" type="image/x-icon" />
    <title>工具——在线正则表达式测试</title>
    <meta name="author" content="尘跃" />
    <meta name="description" content="工具——在线正则表达式测试" />

    <!-- Facebook Open Graph -->
    <meta property="og:site_name" content="" />
    <meta property="og:title" content="工具——在线正则表达式测试" />
    <meta property="og:type" content="website" />
    <meta
        property="og:url"
        content="http://cyue.net/pages/tool/regexp.html"
    />
    <meta property="og:image" content="http://cyue.net/favicon.png" />
    <meta
        property="工具——在线正则表达式测试"
    />

    <!-- Twitter Cards -->
    <meta name="twitter:card" content="summary" />
    <meta name="twitter:image" content="http://cyue.net/favicon.png" />
    <meta name="twitter:title" content="工具——在线正则表达式测试" />
    <meta
        name="twitter:description"
        content="工具——在线正则表达式测试"
    />
    <meta name="twitter:creator" content="@尘跃" />
    <meta name="twitter:site" content="@cyue.net" />

    <!-- style -->
    <link rel="stylesheet/less" type="text/css" href="/css/style.less">
    <!-- less -->
    <script src="/js/third/less.min.js"></script>

    <!-- Vue3 -->
    <script src="/js/vue/vue.global.prod.js"></script>
    <!-- website configuration -->
    <script src="/js/configuration.js"></script>
    <!-- 百度统计 -->
    <script src="/js/tongji.js"></script>

    <style>
        #app {
            display: flex;
            flex-direction: column;
            align-items: center;
            box-sizing: border-box;
        }

        @media (min-width: 25rem) {
            #app {
                align-items: flex-start;
                padding: 1rem 4rem;
            }
        }

        h1 {
            margin-bottom: 0.5em;
        }

        label {
            margin-top: 0.25em;
        }

        input,
        textarea {
            box-sizing: border-box;
            padding: 0.5em;
            margin-top: 0.25em;
        }

        textarea {
            transition: all 50ms;
            width: 16rem;
            height: 10rem;
        }

        /*  */
        #form {
            display: flex;
            flex-direction: column;
            gap: 1em;
        }
        .input-exp {
            width: 16rem;
        }
    </style>
</head>
<body>
    <page-header></page-header>

    <div class="block-spacer"></div>
    <main id="app">
        <h1>正则表达式测试</h1>
        <div>
            <form id="form">
                <label>
                    超时时间
                    <input
                        name="timeout"
                        type="number"
                        placeholder="请输入超时时间"
                        v-model="timeout"
                        style="width: 5em;"
                    />
                    毫秒
                </label>
                <label>
                    最大匹配数
                    <input
                        name="maxMatch"
                        type="number"
                        placeholder="请输入最大匹配数"
                        v-model="maxMatch"
                        style="width: 5em;"
                    >
                </label>
                <div>
                    <label>
                        <input
                            name="ignoreCase"
                            type="checkbox"
                            v-model="ignoreCase"
                        >
                        不区分大小写（i）
                    </label>
                    <label>
                        <input
                            name="multiLine"
                            type="checkbox"
                            v-model="multiLine"
                        >
                        多行匹配（m）
                    </label>
                </div>
                <label>
                    正则表达式
                    <br/>
                    <textarea
                        class="input-exp"
                        name="regexp"
                        type="text"
                        placeholder="请输入正则表达式"
                        v-model="regexp"
                    ></textarea>
                    <br/>
                    <span style="color: red;">{{ expError }}</span>
                </label>
                <label>
                    文本
                    <br/>
                    <textarea
                        name="text"
                        type="text"
                        placeholder="请输入要匹配的文本"
                        v-model="text"
                    ></textarea>
                </label>
                <span>
                    匹配花费了 {{ spendTime }} 毫秒
                </span>
            </form>
        </div>

        <div class="spacer"></div>
        <div style="width: 80%;">
            <h2>匹配</h2>
            <ul id="ul-result">
                <li v-if="result.length === 0">无</li>
                <li v-for="(item, index) in result">
                    匹配{{index + 1}}：
                    <span>
                        {{item.match}}
                    </span>
                    <ul>
                        <li v-for="(g, gName) in item.groups">
                            组“{{gName}}”
                            <span>
                                {{g}}
                            </span>
                        </li>
                    </ul>
                </li>
            </ul>
        </div>
    </main>
    <page-footer></page-footer>

    <!-- web component -->
    <script src="/js/component.js"></script>
    <!-- style -->
    <script src="/js/style.js"></script>
    <!--  -->
    <script>
        const { createApp } = Vue;

        const DEFAULT_TIMEOUT = 300;
        const DEFAULT_MAX_MATCH = 100;
        createApp({
            mounted() {
                const formElement = document.querySelector("#form");
                formElement.onsubmit = (e) => {
                    e.preventDefault();
                }
            },

            data() {
                return {
                    timeout: DEFAULT_TIMEOUT,
                    timeoutExp: "",
                    maxMatch: DEFAULT_MAX_MATCH,
                    ignoreCase: false,
                    multiLine: false,
                    regexp: "",
                    text: "",
                    expError: "",
                    result: [],
                    spendTime: 0,
                }
            },

            watch: {
                regexp() {
                    this.execRegexp();
                },
                text() {
                    this.execRegexp();
                },
                timeout(newV) {
                    const newVNum = parseFloat(newV);
                    if (!newV || isNaN(parseFloat(newV))) {
                        this.timeout = DEFAULT_TIMEOUT;
                    } else {
                        this.timeout = newVNum;
                    }
                },
                maxMatch(newV) {
                    const newVNum = parseFloat(newV);
                    if (!newV || isNaN(parseFloat(newV))) {
                        this.maxMatch = DEFAULT_MAX_MATCH;
                    } else {
                        this.maxMatch = newVNum;
                    }
                }
            },

            methods: {
                copy(text) {
                    navigator.clipboard.writeText(text);
                },
                clear() {
                    this.regexp = "";
                    this.text = "";
                },
                execRegexp() {
                    if (!this.regexp || !this.text) {
                        this.result = [];
                        return;
                    }
                    if (this.regexp === this.timeoutExp) {
                        return;
                    }
                    this.result = [];
                    this.expError = "";
                    this.timeoutExp = "";
                    Promise
                    .resolve()
                    .then(() => {
                        const start = new Date().getTime();
                        let execArray;
                        const flags = `g${this.ignoreCase ? "i" : ""}${this.multiLine ? "m" : ""}`;
                        const regexp = new RegExp(this.regexp, flags);
                        console.log(regexp);
                        while((execArray = regexp.exec(this.text)) !== null) {
                            if (this.result.length >= this.maxMatch) {
                                throw new Error(`匹配数量超过最大匹配数（${this.maxMatch}），请调整正则表达式`);
                            }
                            if (execArray[0]) {
                                this.result.push({
                                    match: execArray[0],
                                    groups: execArray.groups || [],
                                });
                            }
                            if (new Date().getTime() - start > this.timeout) {
                                this.result = [];
                                this.timeoutExp = this.regexp;
                                throw new Error("匹配超时");
                            }
                        }
                        this.spendTime = new Date().getTime() - start;
                        console.log("匹配完成");
                    })
                    .catch((e) => {
                        this.spendTime = 0;
                        this.expError = e.message;
                        console.error("匹配出错", e.message);
                    });
                }
            }
        }).mount("#app");

    </script>
</body>
</html>


